package app

import (
	"duanju-mq/pkg/config"
	"duanju-mq/pkg/log"
	"os"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"strings"
	"sync/atomic"
)

var (
	onStarts  = []func(){}
	startLock = uint32(0)
)

// 所有应用main函数的第一行应为app.Start()
func Start() {
	// 仅在main()里生效
	check_start_caller()
	// 仅可被调用一次
	if atomic.AddUint32(&startLock, 1) != 1 {
		return
	}

	config.PrintUsage()
	if os.Getenv("__CONFIG_DUMP__") != "" {
		dumpConfig()
	}

	for _, fnStart := range onStarts {
		fnStart()
	}
}

// 用于无法使用init()初始化，或要求在main里初始化的情况
func OnStart(fn func()) {
	onStarts = append(onStarts, fn)
}

const start_err = "app.Start() must be called at the first line of main()"

func check_start_caller() {
	ss := strings.Split(string(debug.Stack()), "\n")
	if len(ss) < 4 {
		log.Fatal("Unbelievable!")
	}
	if os.Getenv("__PRINT_START_STACK__") == "1" {
		for i := 0; i < len(ss); i++ {
			log.Info(i, strings.TrimSpace(ss[i]))
		}
	}
	if strings.TrimSpace(ss[len(ss)-3]) != "main.main()" {
		log.Fatal(start_err)
	}
	if strings.TrimSpace(ss[len(ss)-5]) != "duanju-mq/pkg/app.Start()" {
		log.Fatal(start_err)
	}
}

func dumpConfig() {
	dump := "/run/" + filepath.Base(os.Args[0]) + "-" + strconv.Itoa(os.Getpid()) + ".config.dump"
	AsyncSafe(func() {
		<-CTX.Done()
		os.Remove(dump)
	})
	doDump(dump)
	config.OnReload(func() { doDump(dump) })
}

func doDump(f string) {
	usage := config.DumpUsage()
	os.WriteFile(f, []byte(usage), 0644)
}
